1 module backtraced; 2 3 version(Windows) version = UseBacktraced; 4 else version(linux) version = UseBacktraced; 5 6 version(UseBacktraced): 7 debug: 8 import core.demangle; 9 import std.algorithm.searching; 10 11 version (Windows) 12 { 13 14 pragma(lib, "dbghelp.lib"); 15 import core.sys.windows.windows; 16 import core.sys.windows.dbghelp; 17 import core.stdc.stdlib : free, calloc; 18 import core.stdc.stdio : fprintf, stderr; 19 import core.stdc.string : memcpy, strncmp, strlen; 20 import std.string; 21 22 struct SYMBOL_INFO 23 { 24 ULONG SizeOfStruct; 25 ULONG TypeIndex; 26 ULONG64[2] Reserved; 27 ULONG Index; 28 ULONG Size; 29 ULONG64 ModBase; 30 ULONG Flags; 31 ULONG64 Value; 32 ULONG64 Address; 33 ULONG Register; 34 ULONG Scope; 35 ULONG Tag; 36 ULONG NameLen; 37 ULONG MaxNameLen; 38 CHAR[1] Name; 39 } 40 41 extern (Windows) USHORT RtlCaptureStackBackTrace(ULONG FramesToSkip, ULONG FramesToCapture, PVOID* BackTrace, PULONG BackTraceHash); 42 extern (Windows) BOOL SymFromAddr(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, SYMBOL_INFO* Symbol); 43 extern (Windows) BOOL SymGetLineFromAddr64(HANDLE hProcess, DWORD64 dwAddr, PDWORD pdwDisplacement, IMAGEHLP_LINEA64* line); 44 45 46 debug void printStackTrace() 47 { 48 // enum MAX_DEPTH = 256; 49 // void*[MAX_DEPTH] stack; 50 51 // HANDLE process = GetCurrentProcess(); 52 // ushort frames = RtlCaptureStackBackTrace(0, MAX_DEPTH, stack.ptr, null); 53 // SYMBOL_INFO* symbol = cast(SYMBOL_INFO*) calloc((SYMBOL_INFO.sizeof) + 256 * char.sizeof, 1); 54 // symbol.MaxNameLen = 255; 55 // symbol.SizeOfStruct = SYMBOL_INFO.sizeof; 56 57 // IMAGEHLP_LINEA64 line = void; 58 // line.SizeOfStruct = SYMBOL_INFO.sizeof; 59 60 // DWORD dwDisplacement; 61 // 62 // static int ends_with(const(char)* str, const(char)* suffix) 63 // { 64 // if (!str || !suffix) 65 // return 0; 66 // size_t lenstr = strlen(str); 67 // size_t lensuffix = strlen(suffix); 68 // if (lensuffix > lenstr) 69 // return 0; 70 // return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; 71 // } 72 73 // for (uint i = 0; i < frames; i++) 74 // { 75 // SymFromAddr(process, cast(DWORD64)(stack[i]), null, symbol); 76 // SymGetLineFromAddr64(process, cast(DWORD64)(stack[i]), &dwDisplacement, &line); 77 78 // // auto f = frames - i - 1; 79 // char[] funcName = demangle(symbol.Name.ptr[0..symbol.NameLen]); 80 // auto fname = line.FileName; 81 // auto lnum = line.LineNumber; 82 83 // if (ends_with(fname, __FILE__) || funcName.canFind("rt.dmain2._d_run_main2")) 84 // continue; // skip trace from this module 85 86 // fprintf(stderr, "%s:%i - %.*s\n", fname, lnum, cast(int)funcName.length, funcName.ptr); 87 // } 88 89 // free(symbol); 90 } 91 extern (Windows) LONG TopLevelExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) 92 { 93 debug 94 { 95 import hip.util.conv; 96 throw new Exception("Caught Exception (0x"~toHex(pExceptionInfo.ExceptionRecord.ExceptionCode)~")"); 97 } 98 return EXCEPTION_CONTINUE_SEARCH; 99 } 100 101 extern (C) export void backtraced_Register() 102 { 103 debug 104 { 105 SymInitialize(GetCurrentProcess(), null, true); 106 SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_DEBUG); 107 SetUnhandledExceptionFilter(&TopLevelExceptionHandler); 108 } 109 } 110 } 111 112 version (linux) 113 { 114 import core.stdc.signal : SIGSEGV, SIGFPE, SIGILL, SIGABRT, signal; 115 import core.stdc.stdlib : free, exit; 116 import core.stdc.string : strlen, memcpy; 117 import core.stdc.stdio : fprintf, stderr, sprintf, fgets, fclose, FILE; 118 import core.sys.posix.unistd : STDERR_FILENO, readlink; 119 import core.sys.posix.signal : SIGUSR1; 120 import core.sys.posix.stdio : popen, pclose; 121 import core.sys.linux.execinfo : backtrace, backtrace_symbols; 122 import core.sys.linux.dlfcn : dladdr, dladdr1, Dl_info, RTLD_DL_LINKMAP; 123 import core.sys.linux.link : link_map; 124 import core.demangle : demangle; 125 126 extern (C) export void backtraced_Register() 127 { 128 signal(SIGSEGV, &handler); 129 signal(SIGUSR1, &handler); 130 } 131 132 // TODO: clean this mess 133 // TODO: use core.demangle instead 134 extern (C) void handler(int sig) nothrow @nogc 135 { 136 enum MAX_DEPTH = 32; 137 138 string signal_string; 139 switch (sig) 140 { 141 case SIGSEGV: 142 signal_string = "SIGSEGV"; 143 break; 144 case SIGFPE: 145 signal_string = "SIGFPE"; 146 break; 147 case SIGILL: 148 signal_string = "SIGILL"; 149 break; 150 case SIGABRT: 151 signal_string = "SIGABRT"; 152 break; 153 default: 154 signal_string = "unknown"; 155 break; 156 } 157 158 fprintf(stderr, "-------------------------------------------------------------------+\r\n"); 159 fprintf(stderr, "Received signal '%s' (%d)\r\n", signal_string.ptr, sig); 160 fprintf(stderr, "-------------------------------------------------------------------+\r\n"); 161 162 void*[MAX_DEPTH] trace; 163 int stack_depth = backtrace(&trace[0], MAX_DEPTH); 164 char** strings = backtrace_symbols(&trace[0], stack_depth); 165 166 enum BUF_SIZE = 1024; 167 char[BUF_SIZE] syscom = 0; 168 char[BUF_SIZE] my_exe = 0; 169 char[BUF_SIZE] output = 0; 170 171 readlink("/proc/self/exe", &my_exe[0], BUF_SIZE); 172 173 fprintf(stderr, "executable: %s\n", &my_exe[0]); 174 fprintf(stderr, "backtrace: %i\n", stack_depth); 175 176 for (auto i = 2; i < stack_depth; ++i) 177 { 178 auto line = strings[i]; 179 auto len = strlen(line); 180 bool insideParenthesis; 181 int startParenthesis; 182 int endParenthesis; 183 for (int j = 0; j < len; j++) 184 { 185 // () 186 if (!insideParenthesis && line[j] == '(') 187 { 188 insideParenthesis = true; 189 startParenthesis = j + 1; 190 } 191 else if (insideParenthesis && line[j] == ')') 192 { 193 insideParenthesis = false; 194 endParenthesis = j; 195 } 196 } 197 auto addr = convert_to_vma(cast(size_t) trace[i]); 198 FILE* fp; 199 200 auto locLen = sprintf(&syscom[0], "addr2line -e %s %p | ddemangle", &my_exe[0], addr); 201 fp = popen(&syscom[0], "r"); 202 203 auto loc = fgets(&output[0], output.length, fp); 204 fclose(fp); 205 206 // printf("loc: %s\n", loc); 207 208 auto getLen = strlen(output.ptr); 209 210 char[256] func = 0; 211 memcpy(func.ptr, &line[startParenthesis], (endParenthesis - startParenthesis)); 212 sprintf(&syscom[0], "echo '%s' | ddemangle", func.ptr); 213 fp = popen(&syscom[0], "r"); 214 215 output[getLen - 1] = ' '; // strip new line 216 auto locD = fgets(&output[getLen], cast(int)(output.length - getLen), fp); 217 fclose(fp); 218 219 fprintf(stderr, "%s", output.ptr); 220 } 221 exit(-1); 222 } 223 224 // https://stackoverflow.com/questions/56046062/linux-addr2line-command-returns-0/63856113#63856113 225 size_t convert_to_vma(size_t addr) nothrow @nogc 226 { 227 Dl_info info; 228 link_map* link_map; 229 dladdr1(cast(void*) addr, &info, cast(void**)&link_map, RTLD_DL_LINKMAP); 230 return addr - link_map.l_addr; 231 } 232 }